#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <pwd.h>

#define MAX_INPUT_SIZE 2048

// 封装的命令行外壳函数
void os(const char* command, char* output, size_t output_size) {
    // 获取当前用户的信息
    struct passwd* pw = getpwuid(getuid());
    char cwd[1024];

    // 获取当前工作目录
    if (getcwd(cwd, sizeof(cwd)) == NULL) {
        perror("Error getting current directory");
        exit(1);
    }

    // 构建提示信息，包括用户名、当前工作目录和用户输入的命令
    snprintf(output, output_size, "%s:%s$ %s\n", pw->pw_name, cwd, command);

    // 复制用户输入的命令以备后用
    char command_buffer[MAX_INPUT_SIZE];
    strcpy(command_buffer, command);

    // 复制用户输入的命令以备后用（用于管道操作）
    char command_buffer_pipe[MAX_INPUT_SIZE];
    strcpy(command_buffer_pipe, command);

    // 标志，表示是否要在后台运行命令
    int run_in_background = 0;
    if (command_buffer[strlen(command_buffer) - 1] == '&') {
        run_in_background = 1;
        command_buffer[strlen(command_buffer) - 1] = '\0';
    }

    // 输入和输出文件重定向相关
    char* input_file = NULL;
    char* output_file = NULL;
    int pipe_command = 0;

    // 检查是否存在输入重定向符号 '<'
    char* input_redirect = strstr(command, "<");
    if (input_redirect != NULL) {
        *input_redirect = '\0';
        input_redirect += 2;
        input_file = strtok(input_redirect, " ");
    }

    // 检查是否存在输出重定向符号 '>'
    char* output_redirect = strstr(command, ">");
    if (output_redirect != NULL) {
        *output_redirect = '\0';
        output_redirect += 2;
        output_file = strtok(output_redirect, " ");
    }

    // 检查是否存在管道符号 '|'
    char* pipe_symbol = strstr(command, "|");
    if (pipe_symbol != NULL) {
        *pipe_symbol = '\0';
        pipe_symbol += 2;
        pipe_command = 1;
    }

    // 创建子进程
    pid_t pid = fork();
    if (pid < 0) {
        perror("Fork failed");
        exit(1);
    } else if (pid == 0) { // 子进程
        if (input_file) {
            // 打开输入文件并将其连接到标准输入
            int input_fd = open(input_file, O_RDONLY);
            if (input_fd < 0) {
                perror("Input redirection failed");
                exit(1);
            }
            dup2(input_fd, 0);
            close(input_fd);
        }

        if (output_file) {
            // 打开输出文件并将其连接到标准输出
            int output_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
            if (output_fd < 0) {
                perror("Output redirection failed");
                exit(1);
            }
            dup2(output_fd, 1);
            close(output_fd);
        }

        if (pipe_command) {
            // 处理管道命令
            int pipe_fd[2];
            pipe(pipe_fd);
            pid_t pipe_pid = fork();
            if (pipe_pid < 0) {
                perror("Pipe fork failed");
                exit(1);
            } else if (pipe_pid == 0) { // 子进程用于写入管道
                close(pipe_fd[0]);
                dup2(pipe_fd[1], 1);
                close(pipe_fd[1]);

                char* args[64];
                int arg_count = 0;
                char* token = strtok(command_buffer_pipe, " ");
                while (token != NULL) {
                    args[arg_count] = token;
                    arg_count++;
                    token = strtok(NULL, " ");
                }
                args[arg_count] = NULL;
                execvp(args[0], args);
                perror("Pipe command execution failed");
                exit(1);
            } else { // 父进程用于读取管道
                close(pipe_fd[1]);
                dup2(pipe_fd[0], 0);
                close(pipe_fd[0]);

                char* args[64];
                int arg_count = 0;
                char* token = strtok(pipe_symbol, " ");
                while (token != NULL) {
                    args[arg_count] = token;
                    arg_count++;
                    token = strtok(NULL, " ");
                }
                args[arg_count] = NULL;
                execvp(args[0], args);
                perror("Pipe command execution failed");
                exit(1);
            }
        } else {
            // 处理普通命令
            char* args[64];
            int arg_count = 0;
            char* token = strtok(command_buffer, " ");
            while (token != NULL) {
                args[arg_count] = token;
                arg_count++;
                token = strtok(NULL, " ");
            }
            args[arg_count] = NULL;
            execvp(args[0], args);
            perror("Command execution failed");
            exit(1);
        }
    } else { // 父进程
        if (!run_in_background) {
            int status;
            waitpid(pid, &status, 0);
        }
    }

    // 执行命令并获取输出
    char cmd_output[MAX_INPUT_SIZE];
    FILE* fp = popen(command, "r");
    if (fp == NULL) {
        perror("Command execution failed");
        exit(1);
    }

    // 读取命令输出并存储到 cmd_output 中
    if (fgets(cmd_output, sizeof(cmd_output), fp) != NULL) {
        strncpy(output, cmd_output, output_size);
    }

    // 关闭文件流
    pclose(fp);
}

// 回调函数，用于处理按钮点击事件
void on_button_clicked(GtkWidget* widget, gpointer data) {
    GtkTextBuffer* buffer = GTK_TEXT_BUFFER(data);

    // 获取用户输入的命令
    GtkWidget* entry = GTK_WIDGET(g_object_get_data(G_OBJECT(widget), "entry"));
    const gchar* command = gtk_entry_get_text(GTK_ENTRY(entry));

    // 如果用户输入 "exit"，则关闭 GUI 界面并结束程序运行
    if (strcmp(command, "exit") == 0) {
        gtk_main_quit(); // 关闭 GTK 主循环
        return;
    }

    // 创建一个用于存储输出的 GtkTextBuffer
    GtkTextBuffer* output_buffer = gtk_text_buffer_new(NULL);

    // 如果用户输入的是cd命令，执行cd命令并更改当前目录
    if (strncmp(command, "cd ", 3) == 0) {
        const char* new_directory = command + 3; // 去除命令前缀"cd "
        int cd_result = chdir(new_directory);
        if (cd_result == 0) {
            // 成功更改目录
            char cwd[1024];
            if (getcwd(cwd, sizeof(cwd)) == NULL) {
                perror("Error getting current directory");
                exit(1);
            }
            char cd_output[MAX_INPUT_SIZE];
            snprintf(cd_output, sizeof(cd_output), "Changed directory to: %s\n", cwd);
            gtk_text_buffer_insert_at_cursor(output_buffer, cd_output, -1);
        } else {
            // 更改目录失败
            char cd_output[MAX_INPUT_SIZE];
            snprintf(cd_output, sizeof(cd_output), "Failed to change directory to: %s\n", new_directory);
            gtk_text_buffer_insert_at_cursor(output_buffer, cd_output, -1);
        }
    } else {
        // 创建用于读取命令输出的管道
        // popen() 函数通过创建一个管道，调用 fork 产生一个子进程，执行一个 shell 以运行命令来开启一个进程
        FILE* fp = popen(command, "r");
        if (fp == NULL) {
            perror("Command execution failed");
            exit(1);
        }

        // 读取命令输出并将其附加到输出 GtkTextBuffer 中
        char output_line[MAX_INPUT_SIZE];
        while (fgets(output_line, sizeof(output_line), fp) != NULL) {
            gtk_text_buffer_insert_at_cursor(output_buffer, output_line, -1);
        }

        // 关闭文件流
        pclose(fp);
    }

    // 获取输出 GtkTextBuffer 的文本
    GtkTextIter start, end;
    gtk_text_buffer_get_bounds(output_buffer, &start, &end);
    gchar* output_text = gtk_text_buffer_get_text(output_buffer, &start, &end, FALSE);

    // 将输出显示在界面上
    gtk_text_buffer_set_text(buffer, output_text, -1);

    // 释放内存
    g_free(output_text);
    g_object_unref(output_buffer);
}

// 信号处理函数，用于捕获关闭窗口事件
static void on_window_closed(GtkWidget* widget, gpointer data) {
    gtk_main_quit(); // 关闭 GTK 主循环
}

int main(int argc, char* argv[]) {
    // 初始化 GTK
    gtk_init(&argc, &argv);

    // 创建顶级窗口
    GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Mini Shell");
    gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
    gtk_container_set_border_width(GTK_CONTAINER(window), 5);
    g_signal_connect(window, "delete-event", G_CALLBACK(on_window_closed), NULL);

    // 创建文本视图并设置样式
    GtkWidget* text_view = gtk_text_view_new();
    GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
    gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE);
    gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text_view), FALSE);

    // 设置等宽字体和终端颜色风格
    PangoFontDescription* font_desc = pango_font_description_from_string("Monospace 10");
    gtk_widget_override_font(text_view, font_desc);
    gtk_widget_override_background_color(text_view, GTK_STATE_FLAG_NORMAL, &(GdkRGBA){0, 0, 0, 1});    // 黑色背景
    gtk_widget_override_color(text_view, GTK_STATE_FLAG_NORMAL, &(GdkRGBA){0, 1, 0, 1});              // 绿色字体
    pango_font_description_free(font_desc);

    // 将文本视图放入滚动窗口中
    GtkWidget* scrolled_window = gtk_scrolled_window_new(NULL, NULL);
    gtk_container_add(GTK_CONTAINER(scrolled_window), text_view);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

    // 创建输入框和按钮
    GtkWidget* entry = gtk_entry_new();
    gtk_entry_set_placeholder_text(GTK_ENTRY(entry), "Enter command...");
    GtkWidget* button = gtk_button_new_with_label("Run");

    // 将输入框与按钮关联
    g_object_set_data(G_OBJECT(button), "entry", entry);
    g_signal_connect(button, "clicked", G_CALLBACK(on_button_clicked), buffer);

    // 布局管理
    GtkWidget* vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
    gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);

    // 添加到窗口并显示
    gtk_container_add(GTK_CONTAINER(window), vbox);
    gtk_widget_show_all(window);

    // 运行 GTK 主循环
    gtk_main();

    return 0;
}



